﻿//#include rfm

(function() {

var _UNDEFINED,
	_isSupported = _UNDEFINED !== history.pushState && _UNDEFINED != history.replaceState,
	_states = [],
	_currentIndex = -1,
	_lastHash = '',
	_intervalId;

function _setState(index, state, title, url) {
	_currentIndex = index;
	_states[index] = {
			state: state,
			title: title,
			url: url
		});
	document.title = title;
	window.location.hash = _lastHash = '#:' + url + '#?=' + index;
}

function _pushState(state, title, url) {
	if (_isSupported) {
		history.pushState(state, title, url);
	}
	else {
		_setState(_currentIndex++, state, title, url);
	}
}

function _replaceState(state, title, url) {
	if (_isSupported) {
		history.replaceState(state, title, url);
	}
	else {
		_setState(_currentIndex, state, title, url);
	}
}

function _generateEvent(state) {
	var e = document.createEvent('HTMLEvents');
	e.initEvent('popstate', false, false);
	e.state = state;
	window.dispatchEvent(e);
}

var HISTORY_EVENT_NAME = 'rfmHistoryChange',
    _states = [],
    _currentIndex = -1,
    _lastHash = '',
    _intervalId;

function _generateEvent(state) {
    var evt = document.createEvent('HTMLEvents');
    evt.initEvent(HISTORY_EVENT_NAME, false, false);
    evt.$hash = state.hash;
    evt.$url = state.url;
    evt.$data = state.data;
    window.dispatchEvent(evt);
}

function _scan() {
    var winHash = window.location.hash.replace(/^#/, ''),
        state,
        i;
    if (winHash && winHash != _lastHash) {
        for (i = _states.length - 1; i > -1; i--) {
            state = _states[i];
            if (state.hash = winHash) {
                _lastHash = winHash;
                _currentIndex = i;
                _generateEvent(state);
            }
        }
    }
}

rfm.History = {
    /**
     * Listens for a history change event
     * @param {Function} callback The function to execute when a history event occurs. The event object passed to the callback will be extended with the following properties:
     *      $hash {String} The hash key associated with the history state
     *      $url {String} The URL associated with the history state
     *      $data {Object} Raw values associated with the history state
     * @returns {Object} The rfm.History object
     */
    bind: function(callback) {
        window.addEventListener(HISTORY_EVENT_NAME, callback, false);
        return this;
    },
    /**
     * Remove a callback from listening for a history change event
     * @param {Function} callback The function to remove from the event listeners
     * @returns {Object} The rfm.History object
     */
    unbind: function(callback) {
        window.removeEventListener(HISTORY_EVENT_NAME, callback, false);
        return this;
    },
    /**
     * Start scanning for history activity
     * @returns {Object} The rfm.History object
     */
    startScanning: function() {
        rfm.History.stopScanning();
        _scan();
        _intervalId = setInterval(_scan, 50);
        return this;
    },
    /**
     * Stop scanning for history activity
     * @returns {Object} The rfm.History object
     */
    stopScanning: function() {
        clearInterval(_intervalId);
        _intervalId = null;
        return this;
    },
    /**
     * Save a browser history state
     * @param {String} hash The hash key identifying the state
     * @param {String} url A URL to store with the history state (if any)
     * @param {Object} data Data to store with the history state (if any)
     * @returns {Object} The rfm.History object
     */
    push: function(hash, url, data) {
        window.location.hash = _lastHash = hash;
        _currentIndex += 1;
        _states.length = _currentIndex;
        _states[_currentIndex] = {
            hash: hash,
            url: url,
            data: data
        };
    }
};

})();